#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <windows.h>
#include <time.h>
#include <string.h>
#include <stdbool.h>

#define MAP_WIDTH 30        // 游戏地图宽度
#define MAP_HEIGHT 20       // 游戏地图高度
#define SNAKE_MAX_LENGTH 600 // 蛇的最大长度限制
#define HIGHSCORE_FILE "snake_highscore.txt" // 最高分记录文件
// 控制台相关变量（用于图形输出）
HANDLE hConsole = INVALID_HANDLE_VALUE;
CHAR_INFO* consoleBuffer = NULL;
COORD bufferSize = { MAP_WIDTH, MAP_HEIGHT };
COORD bufferCoord = { 0, 0 };
SMALL_RECT writeRegion = { 0, 0, MAP_WIDTH - 1, MAP_HEIGHT - 1 };
// 方向枚举类型
typedef enum {
    DIR_RIGHT, DIR_DOWN, DIR_LEFT, DIR_UP, DIR_NONE
} Direction;
// 游戏状态枚举类型
typedef enum {
    GAME_STATE_MENU,       // 主菜单状态
    GAME_STATE_PLAYING,    // 游戏进行中状态
    GAME_STATE_GAME_OVER,  // 游戏结束状态
    GAME_STATE_EXPLAIN,    // 游戏说明状态
    GAME_STATE_EXIT        // 退出游戏状态
} GameState;
// 位置结构体（表示坐标点）
typedef struct {
    int x;
    int y;
} Position;
// 蛇的结构体定义
typedef struct {
    Position body[SNAKE_MAX_LENGTH]; // 蛇身体各部分的坐标
    int length;                      // 蛇的当前长度
    Direction direction;             // 当前移动方向
    Direction nextDirection;         // 下一次移动方向（防止反向移动）
} Snake;
// 游戏整体状态结构体
typedef struct {
    Position food;         // 食物位置
    Snake snake;           // 蛇的状态
    int score;             // 当前分数
    int highScore;         // 历史最高分
    int baseSpeed;         // 基础速度（毫秒/帧）
    int currentSpeed;      // 当前速度（受加速/减速影响）
    bool isSpeedUp;        // 是否处于加速状态
    bool isSpeedDown;      // 是否处于减速状态
    GameState state;       // 当前游戏状态
} Game;
// 函数声明
void initializeConsole();          // 初始化控制台
void initializeGame(Game* game);    // 初始化游戏
void loadHighScore(Game* game);     // 加载历史最高分
void saveHighScore(Game* game);     // 保存历史最高分
void resetGame(Game* game);         // 重置游戏状态
void generateFood(Game* game);      // 生成食物
bool checkCollision(const Game* game); // 检测碰撞
void updateSnake(Game* game);       // 更新蛇的位置
void processInput(Game* game);      // 处理用户输入
void updateGame(Game* game);        // 更新游戏状态
void renderGame(const Game* game);  // 渲染游戏画面
void showMenu(Game* game);          // 显示主菜单
void showExplain();                 // 显示游戏说明
void showGameOver(Game* game);      // 显示游戏结束界面
void clearScreenBuffer();           // 清空屏幕缓冲区
// 初始化控制台环境（设置窗口大小、隐藏光标）
void initializeConsole() {
    hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
    SMALL_RECT windowSize = { 0, 0, MAP_WIDTH - 1, MAP_HEIGHT + 5 };
    SetConsoleWindowInfo(hConsole, TRUE, &windowSize);
    consoleBuffer = (CHAR_INFO*)malloc(MAP_WIDTH * MAP_HEIGHT * sizeof(CHAR_INFO));
    CONSOLE_CURSOR_INFO cursorInfo;
    GetConsoleCursorInfo(hConsole, &cursorInfo);
    cursorInfo.bVisible = FALSE; // 隐藏光标
    SetConsoleCursorInfo(hConsole, &cursorInfo);
}
// 初始化游戏状态
void initializeGame(Game* game) {
    memset(game, 0, sizeof(Game));
    game->baseSpeed = 320;        // 初始速度（毫秒/帧）
    game->currentSpeed = game->baseSpeed;
    game->isSpeedUp = false;
    game->isSpeedDown = false;
    loadHighScore(game);          // 加载历史最高分
    resetGame(game);              // 重置游戏
    game->state = GAME_STATE_MENU; // 初始状态为菜单
}
// 从文件加载历史最高分
void loadHighScore(Game* game) {
    FILE* file = fopen(HIGHSCORE_FILE, "r");
    if (file) {
        fscanf(file, "%d", &game->highScore);
        fclose(file);
    } else {
        game->highScore = 0; // 文件不存在则初始化为0
    }
}
// 保存历史最高分至文件
void saveHighScore(Game* game) {
    FILE* file = fopen(HIGHSCORE_FILE, "w");
    if (file) {
        fprintf(file, "%d", game->highScore);
        fclose(file);
    }
}
// 重置游戏状态（开始新游戏）
void resetGame(Game* game) {
    // 初始化蛇的状态
    game->snake.length = 5;
    game->snake.direction = DIR_RIGHT;
    game->snake.nextDirection = DIR_RIGHT;
    // 设置蛇的初始位置
    int startX = MAP_WIDTH / 2;
    int startY = MAP_HEIGHT / 2;
    for (int i = 0; i < game->snake.length; i++) {
        game->snake.body[i].x = startX - i; // 蛇身从左到右排列
        game->snake.body[i].y = startY;
    }
    // 重置游戏参数
    game->score = 0;
    game->baseSpeed = 320;
    game->currentSpeed = game->baseSpeed;
    game->isSpeedUp = false;
    game->isSpeedDown = false;
    generateFood(game); // 生成初始食物
}
// 随机生成食物位置（确保不与蛇身重叠）
void generateFood(Game* game) {
    bool validPosition = false;
    while (!validPosition) {
        // 随机生成食物坐标（避开边界）
        game->food.x = rand() % (MAP_WIDTH - 2) + 1;
        game->food.y = rand() % (MAP_HEIGHT - 2) + 1;
        // 检查是否与蛇身重叠
        validPosition = true;
        for (int i = 0; i < game->snake.length; i++) {
            if (game->snake.body[i].x == game->food.x && 
                game->snake.body[i].y == game->food.y) {
                validPosition = false;
                break;
            }
        }
    }
}
// 检查碰撞（边界或自身）
bool checkCollision(const Game* game) {
    Position head = game->snake.body[0];
    // 检查是否撞到边界
    if (head.x <= 0 || head.x >= MAP_WIDTH - 1 || 
        head.y <= 0 || head.y >= MAP_HEIGHT - 1) {
        return true;
    }
    // 检查是否撞到自身
    for (int i = 1; i < game->snake.length; i++) {
        if (head.x == game->snake.body[i].x && head.y == game->snake.body[i].y) {
            return true;
        }
    }
    return false;
}
// 更新蛇的位置和状态
void updateSnake(Game* game) {
    // 处理方向变更
    if ((game->snake.direction == DIR_RIGHT && game->snake.nextDirection != DIR_LEFT) ||
        (game->snake.direction == DIR_LEFT && game->snake.nextDirection != DIR_RIGHT) ||
        (game->snake.direction == DIR_UP && game->snake.nextDirection != DIR_DOWN) ||
        (game->snake.direction == DIR_DOWN && game->snake.nextDirection != DIR_UP)) {
        game->snake.direction = game->snake.nextDirection;
    }
    // 计算新的头部位置
    Position newHead = game->snake.body[0];
    switch (game->snake.direction) {
        case DIR_RIGHT: newHead.x++; break;
        case DIR_LEFT:  newHead.x--; break;
        case DIR_UP:    newHead.y--; break;
        case DIR_DOWN:  newHead.y++; break;
        default: break;
    }
    // 检查是否吃到食物
    bool ateFood = (newHead.x == game->food.x && newHead.y == game->food.y);
    if (!ateFood) {
        // 未吃到食物：移动身体（尾部前移）
        for (int i = game->snake.length - 1; i > 0; i--) {
            game->snake.body[i] = game->snake.body[i - 1];
        }
    } else {
        // 吃到食物：身体增长（尾部保留）
        if (game->snake.length < SNAKE_MAX_LENGTH) {
            game->snake.length++;
            for (int i = game->snake.length - 1; i > 0; i--) {
                game->snake.body[i] = game->snake.body[i - 1];
            }
        }
        // 计算得分（根据速度状态调整）
        int baseScore = 2;
        if (game->isSpeedUp) baseScore += 2;    // 加速状态加4分
        else if (game->isSpeedDown) baseScore -= 1; // 减速状态加1分
        if (baseScore < 0) baseScore = 0;
        game->score += baseScore;
        // 每吃一个食物加速
        if (game->baseSpeed > 60) {
            game->baseSpeed -= 2;
        }
        game->currentSpeed = game->baseSpeed;
        generateFood(game); // 生成新食物
    }
    // 更新头部位置
    game->snake.body[0] = newHead;
}
// 处理用户输入（方向键、功能键）
void processInput(Game* game) {
    game->isSpeedUp = false;
    game->isSpeedDown = false;
    // 检测键盘输入
    if (_kbhit()) {
        int ch = _getch();
        // 处理方向键（扩展键）
        if (ch == 0xE0) {
            int dir = _getch();
            switch (dir) {
                case 72: game->snake.nextDirection = DIR_UP;    break;
                case 80: game->snake.nextDirection = DIR_DOWN;  break;
                case 75: game->snake.nextDirection = DIR_LEFT;  break;
                case 77: game->snake.nextDirection = DIR_RIGHT; break;
            }
        }
        // 处理普通按键
        else {
            switch (ch) {
                case 27: game->state = GAME_STATE_MENU; break; // ESC返回菜单
            }
        }
    }
    // 处理F1/F2功能键（异步检测，支持长按）
    if (GetAsyncKeyState(VK_F1) & 0x8000) {
        game->currentSpeed = 60;    // 加速到最快
        game->isSpeedUp = true;
    } else if (GetAsyncKeyState(VK_F2) & 0x8000) {
        game->currentSpeed = game->baseSpeed + 30; // 减速
        if (game->currentSpeed > 350) game->currentSpeed = 350; // 速度上限
        game->isSpeedDown = true;
    } else {
        game->currentSpeed = game->baseSpeed; // 恢复正常速度
    }
}
// 更新游戏状态（主逻辑）
void updateGame(Game* game) {
    if (game->state != GAME_STATE_PLAYING) return;
    updateSnake(game); // 更新蛇的位置
    // 检测碰撞
    if (checkCollision(game)) {
        if (game->score > game->highScore) {
            game->highScore = game->score; // 更新最高分
            saveHighScore(game);           // 保存最高分
        }
        game->state = GAME_STATE_GAME_OVER; // 切换到游戏结束状态
    }
}
// 清空屏幕缓冲区（准备绘制新画面）
void clearScreenBuffer() {
    if (consoleBuffer) {
        for (int y = 0; y < MAP_HEIGHT; y++) {
            for (int x = 0; x < MAP_WIDTH; x++) {
                consoleBuffer[y * MAP_WIDTH + x].Char.AsciiChar = ' ';
                consoleBuffer[y * MAP_WIDTH + x].Attributes = FOREGROUND_GREEN;
            }
        }
    }
}
// 渲染游戏画面（地图、蛇、食物）
void renderGame(const Game* game) {
    clearScreenBuffer();
    // 绘制地图边界
    for (int x = 0; x < MAP_WIDTH; x++) {
        consoleBuffer[0 * MAP_WIDTH + x].Char.AsciiChar = '#';
        consoleBuffer[0 * MAP_WIDTH + x].Attributes = FOREGROUND_GREEN | FOREGROUND_INTENSITY;
        consoleBuffer[(MAP_HEIGHT - 1) * MAP_WIDTH + x].Char.AsciiChar = '#';
        consoleBuffer[(MAP_HEIGHT - 1) * MAP_WIDTH + x].Attributes = FOREGROUND_GREEN | FOREGROUND_INTENSITY;
    }
    for (int y = 0; y < MAP_HEIGHT; y++) {
        consoleBuffer[y * MAP_WIDTH + 0].Char.AsciiChar = '#';
        consoleBuffer[y * MAP_WIDTH + 0].Attributes = FOREGROUND_GREEN | FOREGROUND_INTENSITY;
        consoleBuffer[y * MAP_WIDTH + (MAP_WIDTH - 1)].Char.AsciiChar = '#';
        consoleBuffer[y * MAP_WIDTH + (MAP_WIDTH - 1)].Attributes = FOREGROUND_GREEN | FOREGROUND_INTENSITY;
    }
    // 绘制蛇
    for (int i = 0; i < game->snake.length; i++) {
        int x = game->snake.body[i].x;
        int y = game->snake.body[i].y;
        consoleBuffer[y * MAP_WIDTH + x].Char.AsciiChar = (i == 0) ? '@' : 'O';
        consoleBuffer[y * MAP_WIDTH + x].Attributes = (i == 0) ? 
            (FOREGROUND_RED | FOREGROUND_INTENSITY) :  // 头部红色高亮
            (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY); // 身体黄色
    }
    // 绘制食物
    consoleBuffer[game->food.y * MAP_WIDTH + game->food.x].Char.AsciiChar = '*';
    consoleBuffer[game->food.y * MAP_WIDTH + game->food.x].Attributes = FOREGROUND_BLUE | FOREGROUND_INTENSITY;
    // 将缓冲区内容输出到控制台
    WriteConsoleOutput(hConsole, consoleBuffer, bufferSize, bufferCoord, &writeRegion);
    // 在地图下方显示游戏信息
    COORD pos = { 0, MAP_HEIGHT };
    SetConsoleCursorPosition(hConsole, pos);
    printf("得分: %-6d  最高分: %-6d  速度: %dms/格", 
    game->score, game->highScore, game->currentSpeed);
    printf("\n控制: 方向键移动 | F1加速 | F2减速 | ESC返回菜单");
}
// 显示主菜单
void showMenu(Game* game) {
    system("cls"); // 清屏
    printf("=== 贪吃蛇游戏 ===\n\n");
    printf("1. 开始游戏\n");
    printf("2. 游戏说明\n");
    printf("3. 退出游戏\n\n");
    printf("当前最高分: %d\n", game->highScore);
    // 获取用户选择
    int choice = _getch();
    while (choice < '1' || choice > '3') {
        printf("\n无效输入，请重新选择 (1-3): ");
        choice = _getch();
    }
    // 根据选择切换游戏状态
    switch (choice) {
        case '1':
            resetGame(game); // 重置游戏
            game->state = GAME_STATE_PLAYING; // 开始游戏
            break;
        case '2':
            game->state = GAME_STATE_EXPLAIN; // 显示游戏说明
            break;
        case '3':
            game->state = GAME_STATE_EXIT; // 退出游戏
            break;
    }
}
// 显示游戏说明
void showExplain() {
    system("cls");
    printf("======== 游戏说明 ========\n");
    printf("得分规则：\n");
    printf("- 正常吃食物：+2分\n");
    printf("- 加速状态吃食物：+4分\n");
    printf("- 减速状态吃食物：+1分\n");
    printf("- 每吃到一个食物，基础速度减2ms\n");
    printf("- 基础速度降到60ms后不再加速\n\n");
    printf("操作控制：\n");
    printf("方向键 - 控制移动方向\n");
    printf("F1    - 临时加速（速度减至60ms）\n");
    printf("F2    - 临时减速（速度+30ms）\n");
    printf("ESC   - 返回主菜单\n");
    printf("\n按任意键返回主菜单...");
    _getch(); // 等待用户按键
}
// 显示游戏结束界面（已修复重新开始时的屏幕残留问题）
void showGameOver(Game* game) {
    system("cls");
    printf("=== 游戏结束 ===\n\n");
    printf("本次得分: %d\n", game->score);
    printf("历史最高: %d\n\n", game->highScore);
    // 根据得分情况显示不同提示
    if (game->score >= game->highScore) {
        printf("创纪录啦！最高分被你刷新啦，真棒！！！\n\n");
    } else if (game->highScore > 0) {
        printf("继续努力吧~ 你离最高分还差: %d 分\n\n", game->highScore - game->score);
    }
    // 显示选项
    printf("1. 重新开始\n");
    printf("2. 返回主菜单\n");
    printf("3. 退出游戏\n");
    // 获取用户选择
    int choice = _getch();
    while (choice < '1' || choice > '3') {
        printf("\n无效输入，请重新选择 (1-3): ");
        choice = _getch();
    }
    // 根据选择切换状态
    switch (choice) {
        case '1':
            resetGame(game); // 重置游戏
            system("cls");   // 关键修复：清除屏幕残留内容
            game->state = GAME_STATE_PLAYING; // 重新开始
            break;
        case '2':
            game->state = GAME_STATE_MENU; // 返回菜单
            break;
        case '3':
            game->state = GAME_STATE_EXIT; // 退出游戏
            break;
    }
}
// 主函数（程序入口点）
int main() {
    srand((unsigned int)time(NULL)); // 初始化随机数种子
    initializeConsole(); // 初始化控制台
    Game game; // 创建游戏状态对象
    initializeGame(&game); // 初始化游戏
    // 主游戏循环
    while (game.state != GAME_STATE_EXIT) {
        // 根据当前游戏状态执行相应逻辑
        switch (game.state) {
            case GAME_STATE_MENU:
                showMenu(&game); // 显示菜单
                break;
            case GAME_STATE_PLAYING:
                processInput(&game); // 处理用户输入
                updateGame(&game);   // 更新游戏状态
                renderGame(&game);   // 渲染游戏画面
                Sleep(game.currentSpeed); // 控制游戏速度
                break;
            case GAME_STATE_EXPLAIN:
                showExplain(); // 显示游戏说明
                game.state = GAME_STATE_MENU; // 说明后返回菜单
                break;
            case GAME_STATE_GAME_OVER:
                showGameOver(&game); // 显示游戏结束界面
                break;
        }
    }
    free(consoleBuffer); // 释放内存
    return 0;
}